package com.yunfinal.api.service.db;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.jfinal.plugin.activerecord.DbPro;
import com.jfinal.plugin.activerecord.Record;
import com.yunfinal.api.service.ApiCheck;
import com.yunfinal.api.service.ApiService;

/**
 * 数据操作
 *
 * @author 杜福忠 2019-01-15 22:32:51
 *
 */
public class DbService extends ApiService {

    /**
     * 默认通用
     */
    public static DbService srvDb = new DbService();

    /**
     * 表名白名单
     */
    public final Set<String> tableNameWhitelist = new HashSet<>();

    public DbService(){}

    /**
     * 表名白名单
     */
    public DbService(String... tableName){
        for (String name : tableName) {
            tableNameWhitelist.add(name);
        }
    }

    /**
     * <pre>
     * {
     * configName : "main", //非必填
     * type : "find | save | delete | update", //非必填
     * tableName : "", // * 必填
     * primaryKey : "id", //非必填
     * data : {
     *      column: ""
     *      }
     * }
     * </pre>
     */
    public Object index(JSONObject kv) {
        String type = getType(kv);
        String tableName = getTableName(kv);
        String primaryKey = getPrimaryKey(kv);
        DbPro pro = getDbPro(kv);

        if ("find".equals(type)) {
            // 数据查询
            return find(pro, kv);
        }

        // 数据 操作
        Record record = new Record().setColumns(kv.getJSONObject("data"));
        if ("save".equals(type)) {
            return save(pro, tableName, primaryKey, record, kv);
        }
        if ("delete".equals(type)) {
            return delete(pro, tableName, primaryKey, record);
        }
        if ("update".equals(type)) {
            return update(pro, tableName, primaryKey, record);
        }
        returnError("F1", "没有该方法" + type, "请参考开发文档");
        return null;
    }


    /**
     * 单表查询
     *
     * <pre>
     * {
     * tableName : "",  //*必填
     * column : [*],   //非必填 默认 *
     * where : [{w:"AND", k:"", x:"=", v:""}],  //非必填， w和x非必填有默认值
     * orderby : "",  //非必填
     * limit : "1",  //非必填,范围 2~200，不填时返回值为对象，大于2时 返回值为 集合对象
     * }
     * </pre>
     */
    protected Object find(DbPro pro, JSONObject kv) {
        StringBuilder sql = new StringBuilder();
        String from = getTableName(kv);

        // SELECT : kv.column
        setSelectSql(kv, sql);

        // FROM : kv.tableName
        sql.append(" FROM ").append(from);

        // WHERE : kv.where
        List<Object> wData = setWhereSql(kv, sql);

        // GROUPBY : kv.groupby
        setGroupbySql(kv, sql);

        // ORDERBY : kv.orderby
        setOrderbySql(kv, sql);

        // LIMIT : kv.limit
        setLimitSql(kv, sql);

        List<Record> list;
        if (null == wData) {
            list = pro.find(sql.toString());
        } else {
            list = pro.find(sql.toString(), wData.toArray());
        }

        if (kv.containsKey("limit")) {
            return list;
        }
        // 如果没有分条数， 就默认1条，直接返回对象
        return list.isEmpty() ? null : list.get(0);
    }

    /**
     * 设置条数 默认1
     */
    protected void setLimitSql(JSONObject kv, StringBuilder sql) {
        String limit = kv.getString("limit");
        if (isBlank(limit)) {
            limit = "1";
        }
        sql.append(" LIMIT ").append(limit);
    }

    /**
     * 设置排序 （orderby 非必填）
     */
    protected void setOrderbySql(JSONObject kv, StringBuilder sql) {
        String orderby = kv.getString("orderby");
        if (isNotBlank(orderby)) {
            sql.append(" ORDER BY ").append(orderby);
        }
    }


    /**
     * 设置分组 （groupby 非必填）
     */
    protected void setGroupbySql(JSONObject kv, StringBuilder sql) {
        String groupby = kv.getString("groupby");
        if (isNotBlank(groupby)) {
            sql.append(" GROUP BY ").append(groupby);
        }
    }

    /**
     * 设置 查询 条件
     *
     * @return
     */
    public JSONArray setWhereVal(JSONObject kv, DbWhere condition){
        JSONArray where = kv.getJSONArray("where");
        if (null == where) {
            where = new JSONArray();
            kv.put("where", where);
        }
        where.add(condition);
        return where;
    }

    /**
     * 设置 查询 条件，默认值 * （where 非必填）
     *
     * @return
     */
    protected List<Object> setWhereSql(JSONObject kv, StringBuilder sql) {
        JSONArray where = kv.getJSONArray("where");
        if (null == where || where.isEmpty()) {
            return null;
        }
        sql.append(" WHERE ");
        boolean sign = false;
        List<Object> ret = new ArrayList<>(where.size());
        for (int i = 0; i < where.size(); i++) {
            JSONObject condition = where.getJSONObject(i);
            String v = condition.getString("v");
            if (isBlank(v)) {
                continue;
            }
            if (sign) {
                String w = condition.getString("w");
                sql.append(isBlank(w) ? "AND" : w);
            } else {
                sign = true;
            }
            // 列字段 名
            String k = condition.getString("k");
            sql.append(" ").append(isBlank(k) ? "" : k);
            // 条件 默认 =
            String x = condition.getString("x");
            sql.append(" ").append(isBlank(x) ? "=" : x);
            // 参数
            sql.append(" ? ");
            // 放入参数
            ret.add(v);
        }
        return ret.isEmpty() ? null : ret;
    }

    /**
     * 设置 查询 字段，默认值 * （column 非必填）
     */
    protected void setSelectSql(JSONObject kv, StringBuilder sql) {
        sql.append("SELECT ");
        JSONArray select = kv.getJSONArray("column");
        if (null == select || select.isEmpty()) {
            sql.append("*");
            return;
        }
        boolean sign = false;
        for (Object column : select) {
            if (sign) {
                sql.append(",");
            } else {
                sign = true;
            }
            setSelectSqlAppend(kv, sql, column);
        }
    }

    /**
     * 设置 查询 字段时的 安全校验
     *
     * @param kv
     * @param sql
     * @param column
     *            字段
     */
    protected void setSelectSqlAppend(JSONObject kv, StringBuilder sql,
            Object column) {
        sql.append(column);
    }

    protected Object update(DbPro pro, String tableName, String primaryKey,
            Record record) {
        return pro.update(tableName, primaryKey, record);
    }

    protected Object delete(DbPro pro, String tableName, String primaryKey,
            Record record) {
        return pro.delete(tableName, primaryKey, record);
    }

    /**
     * 保存<br>
     * 比其他操作 增加了 primaryKeyUUID 参数(非必填项)， 用于 自动充填主键的 UUID
     *
     * @return 主键值
     *
     */
    protected Object save(DbPro pro, String tableName, String primaryKey,
            Record record, JSONObject kv) {
        String[] pKeys = primaryKey.split(",");

        // 自动充填 UUID
        if (true == kv.getBoolean("primaryKeyUUID")) {
            for (int i = 0; i < pKeys.length; i++) {
                pKeys[i] = pKeys[i].trim();
                record.set(pKeys[i], com.jfinal.kit.StrKit.getRandomUUID());
            }
        }

        pro.save(tableName, primaryKey, record);

        if (pKeys.length > 1) {
            for (int i = 0; i < pKeys.length; i++) {
                pKeys[i] = record.get(pKeys[i]);
            }
            return pKeys;
        }

        return record.get(primaryKey);
    }

    /**
     * 主键， 默认值 为 id (非必填项)
     *
     * @param kv
     * @return
     */
    protected String getPrimaryKey(JSONObject kv) {
        String primaryKey = kv.getString("primaryKey");
        if (isBlank(primaryKey)) {
            primaryKey = "id";
        }
        return primaryKey;
    }

    /**
     * 获取数据源 默认为主数据源 (非必填项)
     *
     * @param kv
     * @return
     */
    protected DbPro getDbPro(JSONObject kv) {
        String configName = kv.getString("configName");
        if (isBlank(configName)) {
            configName = com.jfinal.plugin.activerecord.DbKit.MAIN_CONFIG_NAME;
        }
        return com.jfinal.plugin.activerecord.Db.use(configName);
    }

    /**
     * type 操作类型， 默认为 find
     *
     * @param kv
     * @return
     */
    protected String getType(JSONObject kv) {
        String type = kv.getString("type");
        return isBlank(type) ? "find" : type;
    }

    /**
     * 表名必填， 否则 直接返回 400错误，附带 参数名 (必填项)
     *
     * @param kv
     * @return
     */
    protected String getTableName(JSONObject kv) {
        String tableName = ApiCheck.ME.get(kv, "tableName");
        if(tableNameWhitelist.isEmpty() || tableNameWhitelist.contains(tableName)){
            return tableName;
        }
        returnError("403", "非法表名,请参考data参数", tableNameWhitelist);
        return null;
    }

}
